home *** CD-ROM | disk | FTP | other *** search
/ Internet Info 1994 March / Internet Info CD-ROM (Walnut Creek) (March 1994).iso / networking / terms / kermit / b / ckotio.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-06-30  |  44.0 KB  |  1,922 lines

  1. char *ckxv = "OS/2 Communications I/O, 5A(104), 15 May 93";
  2.  
  3. /* C K O T I O  --  Kermit communications I/O support for OS/2 systems */
  4.  
  5. /*
  6.   Also contains code to emulate the UNIX alarm() function under OS/2
  7.   and a set of opendir/readdir/closedir, etc, functions.
  8. */
  9.  
  10. /*
  11.   Author: Frank da Cruz (fdc@columbia.edu, FDCCU@CUVMA.BITNET),
  12.   Columbia University Academic Information Systems, New York City.
  13.  
  14.   Copyright (C) 1985, 1993, Trustees of Columbia University in the City of New
  15.   York.  The C-Kermit software may not be, in whole or in part, licensed or
  16.   sold for profit as a software product itself, nor may it be included in or
  17.   distributed with commercial products or otherwise distributed by commercial
  18.   concerns to their clients or customers without written permission of the
  19.   Office of Kermit Development and Distribution, Columbia University.  This
  20.   copyright notice must not be removed, altered, or obscured.
  21.  
  22.   Originally adapted to OS/2 by Chris Adie (C.Adie@uk.ac.edinburgh),
  23.   Edinburgh University Computing Service, 1988.
  24.  
  25.   Adapted to C-Kermit 5A by Kai Uwe Rommel (rommel@informatik.tu-muenchen.de),
  26.   1992-93.
  27. */
  28.  
  29. /* Includes */
  30.  
  31. #include "ckcdeb.h"            /* Typedefs, debug formats, etc */
  32. #include "ckcker.h"            /* Kermit definitions */
  33. #include "ckcnet.h"            /* Kermit definitions */
  34. #include "ckuxla.h"            /* Translation tables */
  35.  
  36. #include <ctype.h>            /* Character types */
  37. #include <stdio.h>            /* Standard i/o */
  38. #include <io.h>                /* File io function declarations */
  39. #include <fcntl.h>
  40. #include <process.h>            /* Process-control functions */
  41. #include <string.h>            /* String manipulation declarations */
  42. #include <stdlib.h>            /* Standard library declarations */
  43. #include <sys/types.h>
  44. #include <sys/stat.h>
  45. #include <time.h>            /* Time functions */
  46. #include <signal.h>
  47. #include <assert.h>
  48. #include <setjmp.h>
  49.  
  50. #include "ckodir.h"
  51.  
  52. #include "ckuver.h"            /* Version herald */
  53. char ckxsystem[64] = HERALD;
  54. char *ckxsys = ckxsystem;
  55.  
  56. #define DEL 127
  57. #define HUPTIME 500            /* Milliseconds for hangup */
  58.  
  59. #ifndef __32BIT__
  60. #define far _far
  61. #define near _near
  62. #define pascal _pascal
  63. #endif
  64. #define    INCL_NOPM
  65. #define    INCL_ERRORS
  66. #define    INCL_KBD
  67. #define    INCL_DOSMISC
  68. #define    INCL_DOSPROCESS
  69. #define    INCL_DOSQUEUES
  70. #define    INCL_DOSSIGNALS
  71. #define    INCL_DOSDEVICES
  72. #define    INCL_DOSDEVIOCTL
  73. #define    INCL_DOSNLS
  74. #include <os2.h>    /* This pulls in a whole load of stuff */
  75. #undef COMMENT
  76.  
  77. #ifdef CHAR
  78. #undef CHAR
  79. #endif /* CHAR */
  80.  
  81. _PROTOTYP( static int os2setdtr,  (int) );
  82. _PROTOTYP( static int os2setflow, (int) );
  83. _PROTOTYP( static int os2setcarr, (int) );
  84. _PROTOTYP( static int ttsettings, (int, int) );
  85.  
  86. /*============================================================================*/
  87.  
  88. /*
  89.  Variables available to outside world:
  90.  
  91.    dftty  -- Pointer to default tty name string, like "/dev/tty".
  92.    dfloc  -- 0 if dftty is console, 1 if external line.
  93.    dfprty -- Default parity
  94.    dfflow -- Default flow control
  95.    ckxech -- Flag for who echoes console typein:
  96.      1 - The program (system echo is turned off)
  97.      0 - The system (or front end, or terminal).
  98.    functions that want to do their own echoing should check this flag
  99.    before doing so.
  100.  
  101.  Functions for assigned communication line (either external or console tty):
  102.  
  103.    sysinit()               -- System dependent program initialization
  104.    syscleanup()            -- System dependent program shutdown
  105.    ttopen(ttname,local,mdmtyp) -- Open the named tty for exclusive access.
  106.    ttclos()                -- Close & reset the tty, releasing any access lock.
  107.    ttpkt(speed,flow,parity)-- Put the tty in packet mode
  108.                 or in DIALING or CONNECT modem control state.
  109.    ttvt(speed,flow)        -- Put the tty in virtual terminal mode.
  110.    ttinl(dest,max,timo)    -- Timed read line from the tty.
  111.    ttinc(timo)             -- Timed read character from tty.
  112.    ttchk()                 -- See how many characters in tty input buffer.
  113.    ttxin(n,buf)            -- Read n characters from tty (untimed).
  114.    ttol(string,length)     -- Write a string to the tty.
  115.    ttoc(c)                 -- Write a character to the tty.
  116.    ttflui()                -- Flush tty input buffer.
  117.    ttgspd()                -- Speed of tty line.
  118.  
  119. Functions for console terminal:
  120.  
  121.    conraw()  -- Set console into Raw mode
  122.    concooked() -- Set console into Cooked mode
  123.    conoc(c)  -- Unbuffered output, one character to console.
  124.    conol(s)  -- Unbuffered output, null-terminated string to the console.
  125.    conola(s) -- Unbuffered output, array of strings to the console.
  126.    conxo(n,s) -- Unbuffered output, n characters to the console.
  127.    conchk()  -- Check if characters available at console (bsd 4.2).
  128.         Check if escape char (^\) typed at console (System III/V).
  129.    coninc(timo)  -- Timed get a character from the console.
  130.  Following routines are dummies:
  131.    congm()   -- Get console terminal mode.
  132.    concb()   -- Put console into single char mode with no echo.
  133.    conres()  -- Restore console to congm mode.
  134.    conint()  -- Enable console terminal interrupts.
  135.    connoi()  -- No console interrupts.
  136.  
  137. Time functions
  138.  
  139.    sleep(t)  -- Like UNIX sleep
  140.    msleep(m) -- Millisecond sleep
  141.    ztime(&s) -- Return pointer to date/time string
  142.    rtimer() --  Reset timer
  143.    gtimer()  -- Get elapsed time since last call to rtimer()
  144. */
  145.  
  146.  
  147. /* Defines */
  148.  
  149. #define DEVNAMLEN    14
  150.  
  151. /* Declarations */
  152.  
  153. /* dftty is the device name of the default device for file transfer */
  154. /* dfloc is 0 if dftty is the user's console terminal, 1 if an external line */
  155.  
  156. extern long speed;
  157. extern int parity, fcharset, flow, ttcarr;
  158.  
  159. #ifdef COMMENT
  160. /* This is to allow remote operation */
  161.     char *dftty = "0"; /* stdin */
  162.     int dfloc = 0;
  163. #else
  164.     char *dftty = "com1"; /* COM1 */
  165.     int dfloc = 1;
  166. #endif /* COMMENT */
  167.  
  168.     int dfprty = 0;            /* Default parity (0 = none) */
  169.     int ttprty = 0;            /* Parity in use. */
  170.     int ttmdm = 0;            /* Modem in use. */
  171.     int dfflow = FLO_KEEP;        /* Default flow is KEEP, no change*/
  172.     int backgrd = 0;            /* Assume in foreground (no '&' ) */
  173.     int ttcarr = CAR_AUT;        /* Carrier handling mode. */
  174.  
  175. int ckxech = 1; /* 0 if system normally echoes console characters, else 1 */
  176.  
  177. /* Declarations of variables global within this module */
  178.  
  179. static struct rdchbuf_rec {        /* Buffer for serial characters */
  180.     unsigned char buffer[256];
  181.     UINT length, index;
  182. } rdchbuf;
  183.  
  184. char startupdir[CCHMAXPATH] = ".";
  185.  
  186. static long tcount;            /* Elapsed time counter */
  187. static int conmode, consaved;
  188. static char ttnmsv[DEVNAMLEN];
  189. static int islocal, ishandle;
  190. static int pid;
  191.  
  192. #ifdef COMMENT
  193. int ttyfd = 0;        /* TTY file descriptor = stdin */
  194. #else
  195. int ttyfd = -1;        /* TTY file descriptor (not open yet) */
  196. #endif /* COMMENT */
  197.  
  198. DCBINFO ttydcb;
  199.  
  200. extern KEY     *keymap;
  201. extern MACRO     *macrotab;
  202.  
  203. int ttsetspd(long sp);
  204.  
  205. #ifdef __32BIT__
  206.  
  207. USHORT DosDevIOCtl32(PVOID pData, USHORT cbData, PVOID pParms, USHORT cbParms,
  208.              USHORT usFunction, USHORT usCategory, HFILE hDevice)
  209. {
  210.   ULONG ulParmLengthInOut = cbParms, ulDataLengthInOut = cbData;
  211.   return (USHORT) DosDevIOCtl(hDevice, usCategory, usFunction,
  212.                   pParms, cbParms, &ulParmLengthInOut,
  213.                   pData, cbData, &ulDataLengthInOut);
  214. }
  215.  
  216. #define DosDevIOCtl DosDevIOCtl32
  217.  
  218. #else
  219.  
  220. #define DosDevIOCtl DosDevIOCtl2
  221.  
  222. #endif
  223.  
  224.  
  225. void cc_trap(int sig)
  226. {
  227.   signal(sig, cc_trap);
  228. #ifdef __EMX__
  229.   signal(sig, SIG_ACK);
  230. #endif
  231. }
  232.  
  233. /* Saving/restoring of hot handles */
  234.  
  235. static int savedtty = 0;
  236. static long savedspeed;
  237. static LINECONTROL savedlc;
  238. static DCBINFO saveddcb;
  239. static BYTE savedstat;
  240.  
  241. savetty() {
  242.  
  243.   if (ttyfd != -1) {
  244.     savedspeed = ttgspd();
  245.     DosDevIOCtl(&savedlc,sizeof(savedlc),NULL,0,
  246.         ASYNC_GETLINECTRL,IOCTL_ASYNC,ttyfd);
  247.     DosDevIOCtl(&saveddcb,sizeof(saveddcb),NULL,0,
  248.         ASYNC_GETDCBINFO,IOCTL_ASYNC,ttyfd);
  249.     DosDevIOCtl(&savedstat,sizeof(savedstat),NULL,0,
  250.         ASYNC_GETMODEMOUTPUT,IOCTL_ASYNC,ttyfd);
  251.     savedtty = 1;
  252.   }
  253.  
  254.   return 0;
  255. }
  256.  
  257. restoretty() {
  258.   MODEMSTATUS ms;
  259.   UINT data;
  260.  
  261.   if (savedtty) {
  262.     ttsetspd(savedspeed);
  263.     DosDevIOCtl(NULL,0,&savedlc,sizeof(savedlc),
  264.         ASYNC_SETLINECTRL,IOCTL_ASYNC,ttyfd);
  265.     DosDevIOCtl(NULL,0,&saveddcb,sizeof(saveddcb),
  266.         ASYNC_SETDCBINFO,IOCTL_ASYNC,ttyfd);
  267.     ms.fbModemOn = 0;
  268.     ms.fbModemOff = 255;
  269.     if (savedstat & DTR_ON) ms.fbModemOn |= DTR_ON;
  270.     else ms.fbModemOff &= DTR_OFF;
  271.     if (savedstat & RTS_ON) ms.fbModemOn |= RTS_ON;
  272.     else ms.fbModemOff &= RTS_OFF;
  273.     DosDevIOCtl(&data,sizeof(data),&ms,sizeof(ms),
  274.         ASYNC_SETMODEMCTRL,IOCTL_ASYNC,ttyfd);
  275.     savedtty = 0;
  276.   }
  277.  
  278.   return 0;
  279. }
  280.  
  281. /*  S Y S I N I T  --  System-dependent program initialization.  */
  282.  
  283. sysinit() {
  284.     char *ptr;
  285.     int n;
  286. #ifdef __32BIT__
  287.     ULONG cbData, nCodePage[8];
  288.     PTIB pptib;
  289.     PPIB pppib;
  290. #else
  291.     USHORT cbData, nCodePage[8];
  292.     PIDINFO pi;
  293. #endif
  294.  
  295.     if ( DosGetCp(sizeof(nCodePage), nCodePage, &cbData) == 0 )
  296.       switch (nCodePage[0]) {
  297.       case 437:
  298.     fcharset = FC_CP437;
  299.     break;
  300.       case 850:
  301.     fcharset = FC_CP850;
  302.     break;
  303.       case 852:
  304.     fcharset = FC_CP852;
  305.     break;
  306.       }
  307. #ifdef __32BIT__
  308.     DosGetInfoBlocks(&pptib, &pppib);
  309.     pid = pppib -> pib_ulpid;
  310. #else
  311.     DosGetPID(&pi);
  312.     pid = pi.pid;
  313. #endif
  314.     signal(SIGINT, cc_trap);
  315.     signal(SIGBREAK, cc_trap);
  316. #ifdef __32BIT__
  317.     DosError(FERR_DISABLEHARDERR | FERR_DISABLEEXCEPTION);
  318. #else
  319.     DosError(HARDERROR_DISABLE | EXCEPTION_DISABLE);
  320. #endif
  321.     sprintf(ckxsystem, " %s, OS/2 %1d.%02d",
  322. #ifdef __32BIT__
  323.         "32-bit",
  324. #else
  325.         "16-bit",
  326. #endif
  327.             _osmajor / 10, _osminor);
  328. #ifdef __IBMC__
  329.     setvbuf(stdout, NULL, _IONBF, 0);
  330. #endif
  331.     strcpy(startupdir, GetLoadPath());
  332.     if ( (ptr = strrchr(startupdir, '\\')) != NULL )
  333.       *ptr = 0;
  334.     for (ptr = startupdir; *ptr; ptr++)    /* Convert backslashes to slashes */
  335.       if (*ptr == '\\')
  336.     *ptr = '/';
  337.     n = (int)strlen(startupdir);    /* Add slash to end if necessary */
  338.     if (n > -1 && n < CCHMAXPATH)
  339.       if (startupdir[n-1] != '/') {
  340.       startupdir[n] = '/';
  341.       startupdir[n+1] = '\0';
  342.       }
  343. #ifdef COMMENT
  344. /* This is a bit UNIXish and disconcerting */
  345.     strlwr(startupdir);
  346. #endif /* COMMENT */
  347. #ifdef __IBMC__
  348.     setmode(1, O_TEXT);
  349. #endif /* __IBMC__ */
  350.     strcpy(ttnmsv, dftty);
  351.     islocal = isatty(0) && !ttiscom(0);
  352.     if (!islocal) {
  353.       os2setdtr(1);
  354.       ttsettings(ttprty,0);
  355.       os2setflow(flow);
  356.       os2setcarr(ttcarr == CAR_ON);
  357.     }
  358. #ifdef NETCONN
  359.     netinit();
  360. #endif /* NETCONN */
  361.     return(0);
  362. }
  363.  
  364.  
  365. /*  S Y S C L E A N U P  --  System-dependent program cleanup.  */
  366.  
  367. syscleanup() {
  368.     signal(SIGINT, SIG_DFL);
  369.     signal(SIGBREAK, SIG_DFL);
  370.     return(0);
  371. }
  372.  
  373.  
  374. /*  O S 2 S E T F L O W -- set flow state of tty */
  375.  
  376. static int
  377. os2setflow(int nflow) {
  378.     /* Get the current settings */
  379.     if (DosDevIOCtl(&ttydcb,sizeof(ttydcb),NULL,0,
  380.             ASYNC_GETDCBINFO,IOCTL_ASYNC,ttyfd))
  381.         return(-1);
  382.  
  383.     ttydcb.fbCtlHndShake = MODE_DTR_CONTROL; 
  384.     ttydcb.fbFlowReplace &= ~(MODE_AUTO_RECEIVE | MODE_AUTO_TRANSMIT |
  385.     /* clear only a few */    MODE_RTS_CONTROL  | MODE_RTS_HANDSHAKE);
  386.  
  387.     if (nflow == FLO_XONX) {
  388.         ttydcb.fbFlowReplace |= 
  389.       (MODE_AUTO_RECEIVE | MODE_AUTO_TRANSMIT | MODE_RTS_CONTROL);
  390.     }
  391.     else if (nflow == FLO_RTSC) {
  392.         ttydcb.fbCtlHndShake |= MODE_CTS_HANDSHAKE;
  393.     ttydcb.fbFlowReplace |= MODE_RTS_HANDSHAKE;
  394.     }
  395.     else
  396.         ttydcb.fbFlowReplace |= MODE_RTS_CONTROL;
  397.  
  398.     /* Read "some" data from line mode */
  399.     ttydcb.fbTimeout &= ~MODE_NOWAIT_READ_TIMEOUT;
  400.     ttydcb.fbTimeout |= MODE_WAIT_READ_TIMEOUT;
  401.  
  402.     /* Set DCB */
  403.     if (DosDevIOCtl(NULL,0,&ttydcb,sizeof(ttydcb),
  404.             ASYNC_SETDCBINFO,IOCTL_ASYNC,ttyfd))
  405.         return(-1);
  406.  
  407.     if (nflow != FLO_RTSC) {        /* keep RTS permanently on */
  408.         MODEMSTATUS ms;
  409.     UINT data;
  410.     ms.fbModemOn = RTS_ON;
  411.     ms.fbModemOff = 255;
  412.     DosDevIOCtl(&data,sizeof(data),&ms,sizeof(ms),
  413.             ASYNC_SETMODEMCTRL,IOCTL_ASYNC,ttyfd);
  414.     }
  415.  
  416.     return(0);
  417. }
  418.  
  419.  
  420. static int
  421. os2setcarr(int ncarr) {
  422.     /* Get the current settings */
  423.     if (DosDevIOCtl(&ttydcb,sizeof(ttydcb),NULL,0,
  424.             ASYNC_GETDCBINFO,IOCTL_ASYNC,ttyfd))
  425.         return(-1);
  426.  
  427.     if (ncarr)
  428.       ttydcb.fbCtlHndShake |=  MODE_DCD_HANDSHAKE;
  429.     else
  430.       ttydcb.fbCtlHndShake &= ~MODE_DCD_HANDSHAKE;
  431.  
  432.     /* Set DCB */
  433.     if (DosDevIOCtl(NULL,0,&ttydcb,sizeof(ttydcb),
  434.             ASYNC_SETDCBINFO,IOCTL_ASYNC,ttyfd))
  435.         return(-1);
  436.  
  437.     return(0);
  438. }
  439.  
  440.  
  441. /*  O S 2 S E T D T R -- set state of DTR signal */
  442.  
  443. static int
  444. os2setdtr(int on) {
  445.     MODEMSTATUS ms;
  446.     UINT data;
  447.  
  448.     if (ttyfd == -1) return(0);
  449.     ms.fbModemOn = on ? DTR_ON : 0;
  450.     ms.fbModemOff = on ? 255 : DTR_OFF;
  451.     return(DosDevIOCtl(&data,sizeof(data),&ms,sizeof(ms),
  452.                ASYNC_SETMODEMCTRL,IOCTL_ASYNC,ttyfd));
  453. }
  454.  
  455.  
  456. /*  T T S E T T I N G S  --  Set the device driver parity and stop bits */
  457.  
  458. static int
  459. ttsettings(int par, int stop) {
  460.     LINECONTROL lc;
  461.  
  462.     if (DosDevIOCtl(&lc,sizeof(lc),NULL,0,
  463.             ASYNC_GETLINECTRL,IOCTL_ASYNC,ttyfd))
  464.       return(-1); /* Get line */
  465.  
  466.     switch (par) {
  467.     case 'o':
  468.         lc.bDataBits = 7;    /* Data bits */
  469.         lc.bParity   = 1;
  470.         break;
  471.         case 'e':
  472.         lc.bDataBits = 7;    /* Data bits */
  473.         lc.bParity   = 2;
  474.         break;
  475.     case 'm':
  476.         lc.bDataBits = 7;    /* Data bits */
  477.         lc.bParity   = 3;
  478.         break;
  479.     case 's':
  480.         lc.bDataBits = 7;    /* Data bits */
  481.         lc.bParity   = 4;
  482.         break;
  483.     default :
  484.         lc.bDataBits = 8;    /* Data bits */
  485.         lc.bParity   = 0;    /* No parity */
  486.     }
  487.     switch (stop) {
  488.         case 2:
  489.             lc.bStopBits = 2;    /* Two stop bits */
  490.             break;
  491.         case 1:
  492.             lc.bStopBits = 0;    /* One stop bit */
  493.             break;
  494.         default:         /* No change */
  495.             break;
  496.     }
  497.     if (DosDevIOCtl(NULL,0,&lc,sizeof(lc),
  498.             ASYNC_SETLINECTRL,IOCTL_ASYNC,ttyfd))
  499.       return(-1); /* Set line */
  500.     return(0);
  501. }
  502.  
  503. /*  T T O P E N  --  Open a tty for exclusive access.  */
  504.  
  505. /*  Returns 0 on success, -1 on failure.  */
  506. /*
  507.   If called with lcl < 0, sets value of lcl as follows:
  508.   0: the terminal named by ttname is the job's controlling terminal.
  509.   1: the terminal named by ttname is not the job's controlling terminal.
  510.   But watch out: if a line is already open, or if requested line can't
  511.   be opened, then lcl remains (and is returned as) -1.
  512. */
  513. ttopen(char *ttname, int *lcl, int modem, int spare)
  514. {
  515.     char *x; extern char* ttyname();
  516.     char cname[DEVNAMLEN+4];
  517. #ifdef __32BIT__
  518.     ULONG action, res;
  519. #else
  520.     USHORT action, res;
  521. #endif
  522.  
  523.     rdchbuf.length = rdchbuf.index = 0;
  524.  
  525.     if (ttyfd > -1) {            /* if device already opened */
  526.         if (strncmp(ttname,ttnmsv,DEVNAMLEN)) /* are new & old names equal? */
  527.           ttclos(ttyfd);        /* no, close old ttname, open new */
  528.         else                 /* else same, ignore this call, */
  529.       return(0);            /* and return. */
  530.     }
  531.  
  532.     if (*lcl == 0) return(-1);        /* Won't open in local mode */
  533.  
  534.     ishandle = 0;
  535.     ttmdm = modem;            /* Make this available to other fns */
  536.     strcpy(ttnmsv, ttname);        /* Keep copy of name locally. */
  537.  
  538. #ifdef NETCONN
  539.     if (modem < 0) return os2_netopen(ttname, lcl, -modem);
  540. #endif /* NETCONN */
  541.  
  542. /*
  543.   This code lets you give Kermit an open file descriptor for a serial
  544.   communication device, rather than a device name.  Kermit assumes that the
  545.   line is already open, locked, conditioned with the right parameters, etc.
  546. */
  547.     for (x = ttname; isdigit(*x); x++) ; /* Check for all digits */
  548.  
  549.     if (*x == '\0') {
  550.     ttyfd = atoi(ttname);
  551.     ishandle = 1;
  552.     *lcl = 1;            /* Assume it's local. */
  553.     if (ttiscom(ttyfd))
  554.         return savetty();
  555.     ttyfd = -1;
  556.     return(-4);
  557.     }
  558.  
  559.     if (res = DosOpen(ttname,(PHFILE)&ttyfd,&action,0L,0,FILE_OPEN,
  560.                       OPEN_ACCESS_READWRITE | OPEN_SHARE_DENYREADWRITE |
  561.                       OPEN_FLAGS_FAIL_ON_ERROR ,0L)) {
  562.     ttyfd = -1;
  563.     return((res == ERROR_SHARING_VIOLATION) ? -5 : -1);
  564.     }
  565.     debug(F111,"ttopen ok",ttname,*lcl);
  566.  
  567. /* Caller wants us to figure out if line is controlling tty */
  568.     if (*lcl == -1) {
  569.     *lcl = 1;            /* Can never transfer with console */
  570.     }
  571.     if (!ttiscom(ttyfd)) {        /* Not a serial port */
  572.         ttclos(0);
  573.         return(-4);
  574.     }
  575.     savetty();
  576.     ttprty = dfprty;            /* Make parity the default parity */
  577.     if (ttsettings(ttprty,0)) return(-1);
  578.     return(ttflui());
  579. }
  580.  
  581. /*  T T I S C O M  --  Is the given handle an open COM port? */
  582.  
  583. ttiscom(int f) {
  584.     DCBINFO testdcb;
  585.     /* Read DCB */
  586.     if (DosDevIOCtl(&testdcb,sizeof(testdcb),NULL,0,
  587.             ASYNC_GETDCBINFO,IOCTL_ASYNC,f)) {
  588.         return( 0 );            /* Bad, not a serial port */
  589.     }
  590.     return( 1 );            /* Good */
  591. }
  592.  
  593. /*  T T C L O S  --  Close the TTY.  */
  594.  
  595. ttclos(int spare) {
  596. #ifdef NETCONN
  597.     if (ttmdm < 0) return os2_netclos();
  598. #endif /* NETCONN */
  599.     if (ttyfd == -1) return(0);        /* Wasn't open. */
  600.     if (savedtty)
  601.       restoretty();
  602.     if (!ishandle)
  603.       DosClose(ttyfd);
  604.     ishandle = 0;
  605.     ttyfd = -1;
  606.     return(0);
  607. }
  608.  
  609. /*  T T G S P D  --  return speed of tt line, or of default line */
  610.  
  611. long
  612. ttgspd() {
  613.     long sp = 0;
  614.     struct {
  615.       long current_rate;
  616.       char current_fract;
  617.       long minimum_rate;
  618.       char minimum_fract;
  619.       long maximum_rate;
  620.       char maximum_fract;
  621.     } speed;
  622.  
  623.     if (ttyfd == -1) return(-1);
  624.  
  625.     if (DosDevIOCtl(&speed,sizeof(speed),NULL,0,0x0063,IOCTL_ASYNC,ttyfd) == 0)
  626.       return speed.current_rate;
  627.     else
  628.       if (DosDevIOCtl(&sp,sizeof(sp),NULL,0,
  629.               ASYNC_GETBAUDRATE,IOCTL_ASYNC,ttyfd) == 0) 
  630.       return sp;
  631.     else
  632.       return -1;
  633. }
  634.  
  635. ttsetspd(long sp) {
  636.     struct {
  637.       long rate;
  638.       char fract;
  639.     } speed;
  640.  
  641.     if (ttyfd == -1) return(-1);    /* Not open */
  642.  
  643.     if (sp > 65535L) {
  644.       speed.rate = sp;
  645.       speed.fract = 0;
  646.       return DosDevIOCtl(NULL,0,&speed,sizeof(speed),0x0043,IOCTL_ASYNC,ttyfd);
  647.     } else
  648.       return DosDevIOCtl(NULL,0,&sp,sizeof(sp),
  649.              ASYNC_SETBAUDRATE,IOCTL_ASYNC,ttyfd);
  650. }
  651.  
  652.  
  653.  
  654. /*  T T H A N G -- Hangup phone line */
  655.  
  656. tthang() {
  657.     if (os2setdtr(0)) return -1;
  658.     msleep(HUPTIME);
  659.     os2setdtr(1);
  660.     return 1;
  661. }
  662.  
  663.  
  664. /*  T T R E S  --  Restore terminal to "normal" mode.  */
  665.  
  666. ttres() {                /* Restore the tty to normal. */
  667.     if (ttyfd == -1) return(-1);    /* Not open */
  668.     return(0);
  669. }
  670.  
  671.  
  672. /*  T T P K T  --  Condition the communication line for packets. */
  673. /*        or for modem dialing */
  674.  
  675. /*  If called with speed > -1, also set the speed.  */
  676. /*  Returns 0 on success, -1 on failure.  */
  677.  
  678. ttpkt(long speed, int flow, int parity)
  679. {
  680.     int s;
  681.  
  682.     if (ttmdm < 0) return(0);
  683.     if (ttyfd < 0) return(-1);        /* Not open. */
  684.     if (speed < 0) return(-1);
  685.  
  686.     ttprty = parity;
  687.     os2setdtr(1);
  688.     if (ttsetspd(speed)) return(-1);
  689.     if (ttsettings(ttprty,0)) return(-1);
  690.     if (os2setflow(flow)) return(-1);
  691.     if (os2setcarr(ttcarr == CAR_ON && flow != FLO_DIAL)) return(-1);
  692.  
  693.     DosSetPrty(PRTYS_THREAD, PRTYC_FOREGROUNDSERVER, 0, 0);
  694.     return(0);
  695. }
  696.  
  697.  
  698. /*  T T V T -- Condition communication line for use as virtual terminal  */
  699.  
  700. ttvt(long speed, int flow)
  701. {
  702.  
  703.     if (ttmdm < 0) return(0);
  704.     if (ttyfd < 0) return(-1);        /* Not open. */
  705.     if (speed < 0) return(-1);
  706.  
  707.     ttprty = parity;
  708.     os2setdtr(1);
  709.     if (ttsetspd(speed)) return(-1);
  710.     if (ttsettings(ttprty,0)) return(-1);
  711.     if (os2setflow(flow)) return (-1);
  712.     if (os2setcarr(ttcarr == CAR_ON || ttcarr == CAR_AUT)) return(-1);
  713.  
  714.     return(0);
  715. }
  716.  
  717.  
  718. /*  T T S S P D  --  Return the speed if OK, otherwise -1 */
  719.  
  720. int
  721. ttsspd(int speed) {
  722.     long s;
  723.  
  724.     if (speed < 0) return(-1);
  725.  
  726.     s = (long) speed * 10L;
  727.     ttsetspd(s);
  728.     return(0);    
  729. }
  730.  
  731.  
  732. /*  T T F L U I  --  Flush tty input buffer */
  733.  
  734. ttflui() {
  735.     char parm=0;
  736.     long int data;
  737.     int i;
  738.  
  739.     rdchbuf.index = rdchbuf.length = 0;        /* Flush internal buffer */
  740.     DosDevIOCtl(&data,sizeof(data),&parm,sizeof(parm),
  741.         DEV_FLUSHINPUT,IOCTL_GENERAL,ttyfd); /* Flush driver */
  742.     return(0);
  743. }
  744.  
  745.  
  746. /*  T T C H K  --  Tell how many characters are waiting in tty input buffer  */
  747.  
  748. ttchk() {
  749.     USHORT data[2];
  750. #ifdef NETCONN
  751.     if (ttmdm < 0) return os2_nettchk();
  752. #endif /* NETCONN */
  753.     if(DosDevIOCtl(data,sizeof(data),NULL,0,
  754.            ASYNC_GETINQUECOUNT,IOCTL_ASYNC,ttyfd)) return(0);
  755.     else return((rdchbuf.length-rdchbuf.index)+data[0]);
  756. }
  757.  
  758.  
  759. /*  T T X I N  --  Get n characters from tty input buffer  */
  760.  
  761. /*  Returns number of characters actually gotten, or -1 on failure  */
  762.  
  763. /*  Intended for use only when it is known that n characters are actually */
  764. /*  available in the input buffer.  */
  765.  
  766. ttxin(int n, CHAR *buf)
  767. {
  768.     int i, j;
  769.  
  770.     if (ttyfd < 0) return(-1);        /* Not open. */
  771.     i = 0;
  772.     while (i < n) {
  773.         if ((j = ttinc(0)) < 0) break;
  774.         buf[i++] = j;
  775.     }
  776.     return(i);
  777. }
  778.  
  779. /*  T T O L  --  Similar to "ttinl", but for writing.  */
  780.  
  781. ttol(CHAR *s, int n) {
  782.     UINT i;
  783. #ifdef NETCONN
  784.     if (ttmdm < 0) return os2_nettol(s,n);
  785. #endif /* NETCONN */
  786.     if (ttyfd < 0) return(-1);        /* Not open. */
  787.     if (DosWrite(ttyfd,s,n,(PVOID)&i)) return(-1);
  788.     else return(i);
  789. }
  790.  
  791.  
  792. /*  T T O C  --  Output a character to the communication line  */
  793.  
  794. ttoc(char c) {
  795.     UINT i;
  796. #ifdef NETCONN
  797.     if (ttmdm < 0) return os2_nettoc(c);
  798. #endif /* NETCONN */
  799.     if (ttyfd < 0) return(-1);        /* Not open. */
  800.     if(DosWrite(ttyfd,&c,1,(PVOID)&i)) return(-1);
  801.     else return(i);
  802. }
  803.  
  804.  
  805. /*  T T O C I  --  Output a character to the communication line immediately */
  806.  
  807. ttoci(char c) {
  808. #ifdef NETCONN
  809.     if (ttmdm < 0) return os2_nettoc(c);
  810. #endif /* NETCONN */
  811.     if (ttyfd < 0) return(-1);        /* Not open. */
  812.     if(DosDevIOCtl(NULL,0,&c,sizeof(c),
  813.            ASYNC_TRANSMITIMM,IOCTL_ASYNC,ttyfd)) return(-1);
  814.     else return(1);
  815. }
  816.  
  817.  
  818. /*  T T I N L  --  Read a record (up to break character) from comm line.  */
  819. /*
  820.   If no break character encountered within "max", return "max" characters,
  821.   with disposition of any remaining characters undefined.  Otherwise, return
  822.   the characters that were read, including the break character, in "dest" and
  823.   the number of characters read as the value of the function, or 0 upon end of
  824.   file, or -1 if an error occurred.  Times out & returns error if not completed
  825.   within "timo" seconds.
  826. */
  827. ttinl(CHAR *dest, int max, int timo, CHAR eol) {
  828.     int x = 0, c, i, m;
  829.  
  830.     if (ttyfd < 0) return(-1);        /* Not open. */
  831.     *dest = '\0';            /* Clear destination buffer */
  832.     i = 0;                /* Next char to process */
  833.     while (1) {
  834.     if ((c = ttinc(timo)) == -1) {
  835.         x = -1;
  836.         break;
  837.     }
  838.         dest[i] = c;            /* Got one. */
  839.     if (dest[i] == eol) {
  840.         dest[++i] = '\0';
  841.         return(i);
  842.     }
  843.     if (i++ > max) {
  844.         debug(F101,"ttinl buffer overflow","",i);
  845.         x = -1;
  846.         break;
  847.     }
  848.     }
  849.     debug(F100,"ttinl timout","",0);    /* Get here on timeout. */
  850.     debug(F111," with",dest,i);
  851.     return(x);                /* and return error code. */
  852. }
  853.  
  854.  
  855. /*  T T I N C --  Read a character from the communication line  */
  856.  
  857. /* The time should be in secs for consistency with the other modules in    */
  858. /* kermit.  To retain the option of using times of less than 1s a negative */
  859. /* parameter is interpreted as meaning multiples of 0.01s                  */
  860.  
  861. static rdch(void);
  862.  
  863. ttinc(timo) int timo; {
  864.     int m, i;
  865.     char ch = 0;
  866. #ifdef NETCONN
  867.     if (ttmdm < 0) return os2_netinc(timo);
  868. #endif /* NETCONN */
  869.     m = (ttprty) ? 0177 : 0377;        /* Parity stripping mask. */
  870.     if (ttyfd < 0) return(-1);        /* Not open. */
  871.  
  872.     if (timo == 0) {            /* Untimed. */
  873.  
  874.         if (ttydcb.usReadTimeout != 9) {
  875.         ttydcb.usReadTimeout = 9;    /* Test every  0.1s per call */
  876.         if (DosDevIOCtl(NULL,0,&ttydcb,sizeof(ttydcb),
  877.                 ASYNC_SETDCBINFO,1,ttyfd))
  878.           return(-1);
  879.     }
  880.  
  881.         do
  882.           i = rdch();
  883.         while (i < 0);   /* Wait for a character. */
  884.  
  885.         return(i & m);
  886.     }
  887.  
  888.     if (timo < 0)
  889.         timo= -timo - 1;
  890.     else
  891.         timo = timo * 100 - 1;
  892.  
  893.     if (ttydcb.usReadTimeout != timo) { /* Set timeout value */
  894.        ttydcb.usReadTimeout = timo;
  895.        if (DosDevIOCtl(NULL,0,&ttydcb,sizeof(ttydcb),
  896.                ASYNC_SETDCBINFO,IOCTL_ASYNC,ttyfd))
  897.           return(-1);
  898.     }
  899.  
  900.     i = rdch();
  901.  
  902.     if (i < 0) return(-1);
  903.     else return(i & m);
  904. }
  905.  
  906. /*  RDCH -- Read characters from the serial port, maintaining an internal
  907.             buffer of characters for the sake of efficiency. */
  908. static rdch() {
  909.  
  910.     if (rdchbuf.index == rdchbuf.length) {
  911.     rdchbuf.index = 0;
  912.         if (DosRead(ttyfd,rdchbuf.buffer,sizeof(rdchbuf.buffer),
  913. #ifdef __32BIT__
  914.                     (PULONG) &rdchbuf.length)) {
  915. #else
  916.                     (PUSHORT) &rdchbuf.length)) {
  917. #endif
  918.         rdchbuf.length = 0;
  919.         return(-1);
  920.         }
  921.     }
  922.  
  923.     return( (rdchbuf.index < rdchbuf.length)
  924.             ? rdchbuf.buffer[rdchbuf.index++] : -1 );
  925. }
  926.  
  927. /*  T T S C A R R  --  Set ttcarr variable, controlling carrier handling.
  928.  *
  929.  *  0 = Off: Always ignore carrier. E.g. you can connect without carrier.
  930.  *  1 = On: Heed carrier, except during dialing. Carrier loss gives disconnect.
  931.  *  2 = Auto: For "modem direct": The same as "Off".
  932.  *            For real modem types: Heed carrier during connect, but ignore
  933.  *                it anytime else.  Compatible with pre-5A C-Kermit versions.
  934.  */
  935.  
  936. int
  937. ttscarr(carrier) int carrier; {
  938.     ttcarr = carrier;
  939.     debug(F101, "ttscarr","",ttcarr);
  940.     return(ttcarr);
  941. }
  942.  
  943. /*  T T G M D M  --  Get modem signals  */
  944. /*
  945.  Looks for the modem signals CTS, DSR, and CTS, and returns those that are
  946.  on in as its return value, in a bit mask as described for ttwmdm.  Returns:
  947.  -3 Not implemented
  948.  -2 if the line does not have modem control
  949.  -1 on error.
  950.  >= 0 on success, with a bit mask containing the modem signals that are on.
  951. */
  952.  
  953. int
  954. ttgmdm() {
  955.     BYTE instat, outstat;
  956.     int modem = 0;
  957.  
  958.     if(DosDevIOCtl(&instat,sizeof(instat),NULL,0,
  959.            ASYNC_GETMODEMINPUT,IOCTL_ASYNC,ttyfd))
  960.        return(-1);
  961.     if(DosDevIOCtl(&outstat,sizeof(outstat),NULL,0,
  962.            ASYNC_GETMODEMOUTPUT,IOCTL_ASYNC,ttyfd))
  963.        return(-1);
  964.  
  965.     /* Clear To Send */
  966.     if (instat & CTS_ON) modem |= BM_CTS;
  967.     /* Data Set Ready */
  968.     if (instat & DSR_ON) modem |= BM_DSR;
  969.     /* Carrier */
  970.     if (instat & DCD_ON) modem |= BM_DCD;
  971.     /* Ring Indicate */
  972.     if (instat & RI_ON)  modem |= BM_RNG;
  973.  
  974.     /* Data Terminal Ready */
  975.     if (outstat & DTR_ON) modem |= BM_DTR;
  976.     /* Request To Send */
  977.     if (outstat & RTS_ON) modem |= BM_RTS;
  978.  
  979.     return(modem);
  980. }
  981.  
  982. /*  T T S N D B  --  Send a BREAK signal  */
  983.  
  984. ttsndb() {
  985.     MODEMSTATUS ms;
  986.     UINT data, i;
  987. #ifdef NETCONN
  988.     if (ttmdm < 0) return os2_netbreak();
  989. #endif /* NETCONN */
  990.     ms.fbModemOn = RTS_ON;
  991.     ms.fbModemOff = 255;
  992.     DosDevIOCtl(&data,sizeof(data),&ms,sizeof(ms),
  993.         ASYNC_SETMODEMCTRL,IOCTL_ASYNC,ttyfd);
  994.  
  995.     DosDevIOCtl(&i,sizeof(i),NULL,0,
  996.         ASYNC_SETBREAKON,IOCTL_ASYNC,ttyfd);    /* Break on */
  997.     DosSleep(275L);                    /* ZZZzzz */
  998.     DosDevIOCtl(&i,sizeof(i),NULL,0,
  999.         ASYNC_SETBREAKOFF,IOCTL_ASYNC,ttyfd);    /* Break off */
  1000. }
  1001.  
  1002. /*  T T S N D L B  --  Send a LONG BREAK signal  */
  1003.  
  1004. ttsndlb() {
  1005.     MODEMSTATUS ms;
  1006.     UINT data, i;
  1007. #ifdef NETCONN
  1008.     if (ttmdm < 0) return os2_netbreak();
  1009. #endif /* NETCONN */
  1010.     ms.fbModemOn = RTS_ON;
  1011.     ms.fbModemOff = 255;
  1012.     DosDevIOCtl(&data,sizeof(data),&ms,sizeof(ms),
  1013.         ASYNC_SETMODEMCTRL,IOCTL_ASYNC,ttyfd);
  1014.  
  1015.     DosDevIOCtl(&i,sizeof(i),NULL,0,
  1016.         ASYNC_SETBREAKON,IOCTL_ASYNC,ttyfd);    /* Break on */
  1017.     DosSleep(1800L);                    /* ZZZzzz */
  1018.     DosDevIOCtl(&i,sizeof(i),NULL,0,
  1019.         ASYNC_SETBREAKOFF,IOCTL_ASYNC,ttyfd);    /* Break off */
  1020. }
  1021.  
  1022.  
  1023. #ifndef __EMX__
  1024. /*  S L E E P  --  Emulate the Unix sleep function  */
  1025.  
  1026. unsigned sleep(t) unsigned t; {
  1027.     DosSleep((long)t*1000);
  1028. }
  1029. #endif
  1030.  
  1031.  
  1032. /*  M S L E E P  --  Millisecond version of sleep().  */
  1033.  
  1034. /* Intended only for small intervals.  For big ones, just use sleep(). */
  1035.  
  1036. msleep(m) int m; {
  1037.     DosSleep((long)m);
  1038. }
  1039.  
  1040.  
  1041. /*  R T I M E R --  Reset elapsed time counter  */
  1042.  
  1043. void rtimer() {
  1044.     tcount = time((long *)NULL);
  1045. }
  1046.  
  1047.  
  1048. /*  G T I M E R --  Get current value of elapsed time counter in seconds  */
  1049.  
  1050. int gtimer(void) {
  1051.     int x;
  1052.     x = (int) (time( (long *) 0 ) - tcount);
  1053.     return( (x < 0) ? 0 : x );
  1054. }
  1055.  
  1056.  
  1057. /*  Z T I M E  --  Return date/time string  */
  1058.  
  1059. void ztime(char **s) {
  1060.     long clock_storage;
  1061.  
  1062.     clock_storage = time( (long *) 0 );
  1063.     *s = ctime( &clock_storage );
  1064. }
  1065.  
  1066. /*  C O N O C  --  Output a character to the console terminal  */
  1067.  
  1068. int conoc(char c) {
  1069.     write(1,&c,1);
  1070. }
  1071.  
  1072.  
  1073. /*  C O N X O  --  Write x characters to the console terminal  */
  1074.  
  1075. int conxo(int x, char *s) {
  1076.     write(1,s,x);
  1077. }
  1078.  
  1079.  
  1080. /*  C O N O L  --  Write a line to the console terminal  */
  1081.  
  1082. int conol(char *s) {
  1083.     int len;
  1084.     len = strlen(s);
  1085.     write(1,s,len);
  1086. }
  1087.  
  1088.  
  1089. /*  C O N O L A  --  Write an array of lines to the console terminal */
  1090.  
  1091. conola(s) char *s[]; {
  1092.     int i;
  1093.     for (i=0 ; *s[i] ; i++) conol(s[i]);
  1094. }
  1095.  
  1096.  
  1097. /*  C O N O L L  --  Output a string followed by CRLF  */
  1098.  
  1099. conoll(s) char *s; {
  1100.     conol(s);
  1101.     write(1,"\r\n",2);
  1102. }
  1103.  
  1104.  
  1105. /*  C O N C H K  --  Return how many characters available at console  */
  1106.  
  1107. conchk() {
  1108.     KBDKEYINFO k;
  1109.  
  1110.     KbdPeek(&k,0);
  1111.     return( (k.fbStatus & 0x40) ? 1 : 0 );
  1112. }
  1113.  
  1114.  
  1115. /*  C O N I N C  --  Get a character from the console  */
  1116.  
  1117. coninc(timo) int timo; {
  1118.     int c;
  1119.  
  1120.     while ( (c = congks(timo)) >= 0x100 )
  1121.         if ( c > 0x200 && isdigit(c & 0xFF) )
  1122.             break;
  1123.     return (c & 0xFF);
  1124. }
  1125.  
  1126. static jmp_buf kbbuf;
  1127. SIGTYP (*saval)(int);
  1128.  
  1129. SIGTYP kbdtimo(int sig)
  1130. {
  1131. #ifdef __EMX__
  1132.     signal(SIGALRM, SIG_ACK);
  1133. #endif
  1134.     signal(SIGALRM, saval);
  1135.     longjmp(kbbuf, 1);
  1136. }
  1137.  
  1138. #ifndef SHIFT_KEY_IN
  1139. #define SHIFT_KEY_IN    KBDTRF_SHIFT_KEY_IN
  1140. #endif /* SHIFT_KEY_IN */
  1141.  
  1142. #ifndef CONTROL
  1143. #define CONTROL         KBDSTF_CONTROL
  1144. #define SCROLLLOCK      KBDSTF_SCROLLLOCK
  1145. #define SCROLLLOCK_ON   KBDSTF_SCROLLLOCK_ON
  1146. #define NUMLOCK         KBDSTF_NUMLOCK
  1147. #define NUMLOCK_ON      KBDSTF_NUMLOCK_ON
  1148. #endif /* CONTROL */
  1149.  
  1150. congks(timo) int timo; {
  1151.     /* timeout isn't handled */
  1152.     KBDKEYINFO k;
  1153.     int c;
  1154.  
  1155.     if (!islocal) {
  1156.       c = 0;
  1157.       if ( read(ttyfd, &c, 1) < 1 )
  1158.     return -1;
  1159.       return c;
  1160.     };
  1161.  
  1162.     for (;;)
  1163.     {
  1164.         if (timo <= 0)
  1165.       KbdCharIn(&k, IO_WAIT, 0);
  1166.     else 
  1167.     {
  1168.       saval = signal(SIGALRM, kbdtimo);
  1169.       alarm(timo);
  1170.       
  1171.       if (setjmp(kbbuf))
  1172.         return -1;
  1173.       else
  1174.         KbdCharIn(&k, IO_WAIT, 0);
  1175.  
  1176.       alarm(0);
  1177.       signal(SIGALRM, saval);
  1178.     }
  1179.  
  1180.         if ( k.chChar || k.chScan )
  1181.         {
  1182.             c = k.chChar;
  1183.  
  1184.             if (c == 0x00) c = 0x100 | k.chScan;
  1185.             if (c == 0xE0) c = 0x200 | k.chScan;
  1186.  
  1187.         switch (c) /* handle ambiguous keypad and space keys */
  1188.         {
  1189.         case '\b':
  1190.           return k.chScan == 0x0E ? DEL : c;
  1191.         case DEL:
  1192.           return 0x200 | c;
  1193.         case ' ':
  1194.           return (k.fsState & CONTROL) ? 0x200 | c : c;
  1195.         case '+':
  1196.           return k.chScan == 0x4E ? 0x200 | c : c;
  1197.         case '-':
  1198.           return k.chScan == 0x4A ? 0x200 | c : c;
  1199.         case '*':
  1200.           return k.chScan == 0x37 ? 0x200 | c : c;
  1201.         case '/':
  1202.           return k.chScan == 0xE0 ? 0x200 | c : c;
  1203.         case '\r':
  1204.         case '\n':
  1205.           return k.chScan == 0xE0 ? 0x200 | c : c;
  1206.         case ',':
  1207.           return k.chScan == 0x53 ? 0x200 | c : c;
  1208.         case '0':
  1209.         case '1':
  1210.         case '2':
  1211.         case '3':
  1212.         case '4':
  1213.         case '5':
  1214.         case '6':
  1215.         case '7':
  1216.         case '8':
  1217.         case '9':
  1218.           return k.chScan >= 0x47 ? 0x200 | c : c;
  1219.         default:
  1220.           return c;
  1221.         }
  1222.         }
  1223.         if ( (k.fbStatus & SHIFT_KEY_IN) && (k.fsState & NUMLOCK) )
  1224.             return (k.fsState & NUMLOCK_ON) ? 0x2FE : 0x1FE;
  1225.  
  1226.         if ( (k.fbStatus & SHIFT_KEY_IN) && (k.fsState & SCROLLLOCK) )
  1227.             return (k.fsState & SCROLLLOCK_ON) ? 0x2FF : 0x1FF;
  1228.     }
  1229. }
  1230.  
  1231. conraw() {
  1232.     KBDINFO k;
  1233.  
  1234.     if (!islocal) return(0);
  1235.     conmode = 1;
  1236.     k.cb = sizeof(k);
  1237.     KbdGetStatus(&k,0);
  1238.     k.fsMask = KEYBOARD_ECHO_OFF
  1239.              | KEYBOARD_BINARY_MODE
  1240.              | KEYBOARD_SHIFT_REPORT;
  1241.     return(KbdSetStatus(&k,0));
  1242. }
  1243.  
  1244. concooked() {
  1245.     KBDINFO k;
  1246.  
  1247.     if (!islocal) return(0);
  1248.     conmode = 0;
  1249.     k.cb = sizeof(k);
  1250.     KbdGetStatus(&k,0);
  1251.     k.fsMask = KEYBOARD_ECHO_ON
  1252.              | KEYBOARD_ASCII_MODE;
  1253.     return(KbdSetStatus(&k,0));
  1254. }
  1255.  
  1256.  
  1257. /* init key map */
  1258.  
  1259. void keymapinit() {
  1260.     /* predefine NUL characters */
  1261.     keymap[0x103] = 0x00;
  1262.     keymap[0x200 | DEL ] = DEL;
  1263.     keymap[0x200 | ' ' ] = 0x00;
  1264.     /* predefine ambiguous keypad keys */
  1265.     keymap[0x200 | '+' ] = '+';
  1266.     keymap[0x200 | '-' ] = '-';
  1267.     keymap[0x200 | '*' ] = '*';
  1268.     keymap[0x200 | '/' ] = '/';
  1269.     keymap[0x200 | '\r'] = '\r';
  1270.     keymap[0x200 | '\n'] = '\n';
  1271.     keymap[0x200 | ',' ] = ',';
  1272.     keymap[0x200 | '0' ] = '0';
  1273.     keymap[0x200 | '1' ] = '1';
  1274.     keymap[0x200 | '2' ] = '2';
  1275.     keymap[0x200 | '3' ] = '3';
  1276.     keymap[0x200 | '4' ] = '4';
  1277.     keymap[0x200 | '5' ] = '5';
  1278.     keymap[0x200 | '6' ] = '6';
  1279.     keymap[0x200 | '7' ] = '7';
  1280.     keymap[0x200 | '8' ] = '8';
  1281.     keymap[0x200 | '9' ] = '9';
  1282. }
  1283.  
  1284. /*  C O N B I N  --  Put console in binary mode  */
  1285.  
  1286. /*  Returns 0 if ok, -1 if not  */
  1287.  
  1288. conbin(char esc) {
  1289.     if (!islocal) return(0);          /* only for real ttys */
  1290.     conraw();
  1291. }
  1292.  
  1293. /*  C O N C B  -- Put console into single char mode with no echo. */
  1294.  
  1295. concb(char esc) {
  1296.     if (!islocal) return(0);          /* only for real ttys */
  1297.     concooked();
  1298. }
  1299.  
  1300.  
  1301. /*  C O N G M  -- Get console terminal mode. */
  1302.  
  1303. congm() {}
  1304.  
  1305. /*  C O N R E S -- Restore console to congm mode. */
  1306.  
  1307. conres() {}
  1308.  
  1309.  
  1310. /*  C O N I N T -- Enable console terminal interrupts. */
  1311.  
  1312. void conint(f, s) SIGTYP (*f)(int), (*s)(int); {
  1313.     signal(SIGINT, f);
  1314.     signal(SIGBREAK, f);
  1315. }
  1316.  
  1317.  
  1318. /*  C O N N O I -- No console interrupts. */
  1319.  
  1320. void connoi() {
  1321.     signal(SIGINT, cc_trap);
  1322.     signal(SIGBREAK, cc_trap);
  1323. }
  1324.  
  1325.  
  1326. /* privilege dummy */
  1327.  
  1328. int priv_chk() {return 0;}
  1329.  
  1330.  
  1331. #ifndef __EMX__
  1332.  
  1333. /* alarm() implementation for all others, emx/gcc already has it built-in */
  1334.  
  1335. #ifdef __32BIT__
  1336.  
  1337. #define STACK 8192
  1338. static BOOL alrm, running;
  1339. static UINT delay;
  1340.  
  1341. static long FAR alarm_thread(VOID *args)
  1342. {
  1343.   for (;;)
  1344.   {
  1345.     DosSleep(1000L);
  1346.     DosEnterCritSec();
  1347.  
  1348.     if ( alrm )
  1349.       if ( --delay == 0 ) {
  1350.     alrm = FALSE;
  1351.     DosKillProcess(DKP_PROCESS, pid);
  1352.       }
  1353.  
  1354.     DosExitCritSec();
  1355.   }
  1356.  
  1357.   running = FALSE;
  1358.   DosExit(EXIT_THREAD, 0);
  1359. }
  1360.  
  1361. static void alarm_signal(int sig)
  1362. {
  1363.   signal(SIGTERM, SIG_DFL); /* avoid making us kill-proof all the time */
  1364.   /* DosBeep(440, 10); */
  1365.   raise(SIGALRM);
  1366. }
  1367.  
  1368. unsigned alarm(unsigned sec)
  1369. {
  1370.   TID tid;
  1371.   unsigned old;
  1372.  
  1373.   DosEnterCritSec();
  1374.  
  1375.   old = delay;
  1376.   delay = sec;
  1377.  
  1378.   if ( alrm = (delay > 0) )
  1379.     signal(SIGTERM, alarm_signal);
  1380.  
  1381.   DosExitCritSec();
  1382.  
  1383.   if ( !running )
  1384.   {
  1385.     running = TRUE;
  1386.     DosCreateThread(&tid, (PFNTHREAD)alarm_thread, 0, 0, STACK);
  1387.     DosSetPrty(PRTYS_THREAD, PRTYC_REGULAR, 0, tid);
  1388.   }
  1389.  
  1390.   return old;
  1391. }
  1392.  
  1393. #else
  1394.  
  1395. #define STACK 2048
  1396. static PBYTE pstack;
  1397. static BOOL alrm, running;
  1398. static USHORT delay;
  1399.  
  1400. #pragma check_stack(off)
  1401.  
  1402. static VOID FAR alarm_thread(VOID)
  1403. {
  1404.   for (;;)
  1405.   {
  1406.     DosSleep(1000L);
  1407.     DosEnterCritSec();
  1408.  
  1409.     if ( alrm )
  1410.       if ( --delay == 0 ) {
  1411.     alrm = FALSE;
  1412.     DosFlagProcess(pid, FLGP_PID, PFLG_A, 1);
  1413.       }
  1414.  
  1415.     DosExitCritSec();
  1416.   }
  1417.  
  1418.   running = FALSE;
  1419.   DosExit(EXIT_THREAD, 0);
  1420. }
  1421.  
  1422. #pragma check_stack()
  1423.  
  1424. static VOID PASCAL FAR alarm_signal(USHORT sigarg, USHORT signum)
  1425. {
  1426.   PFNSIGHANDLER prev;
  1427.   USHORT action;
  1428.   DosSetSigHandler(alarm_signal, &prev, &action, SIGA_ACKNOWLEDGE, SIG_PFLG_A);
  1429.   raise(SIGALRM);
  1430. }
  1431.  
  1432. unsigned alarm(unsigned sec)
  1433. {
  1434.   PFNSIGHANDLER prev;
  1435.   USHORT action;
  1436.   TID tid;
  1437.   unsigned old;
  1438.  
  1439.   if ( pstack == NULL )
  1440.   {
  1441.     pstack = malloc(STACK);
  1442.     assert(pstack != NULL);
  1443.     DosSetSigHandler(alarm_signal, &prev, &action, SIGA_ACCEPT, SIG_PFLG_A);
  1444.   }
  1445.  
  1446.   DosEnterCritSec();
  1447.  
  1448.   old = delay;
  1449.   delay = sec;
  1450.   alrm = (delay > 0);
  1451.  
  1452.   DosExitCritSec();
  1453.  
  1454.   if ( !running )
  1455.   {
  1456.     running = TRUE;
  1457.     DosCreateThread(alarm_thread, &tid, pstack + STACK);
  1458.     DosSetPrty(PRTYS_THREAD, PRTYC_REGULAR, 0, tid);
  1459.   }
  1460.  
  1461.   return old;
  1462. }
  1463.  
  1464. #endif
  1465.  
  1466.  
  1467. /*
  1468.  *  A public domain implementation of BSD directory routines for
  1469.  *  MS-DOS.  Written by Michael Rendell ({uunet,utai}michael@garfield),
  1470.  *  August 1897
  1471.  *  Ported to OS/2 by Kai Uwe Rommel
  1472.  *  December 1989, February 1990
  1473.  *  Change for HPFS support, October 1990
  1474.  */
  1475.  
  1476. int attributes = A_DIR | A_HIDDEN;
  1477.  
  1478. static char *getdirent(char *);
  1479. static void free_dircontents(struct _dircontents *);
  1480.  
  1481. static HDIR hdir;
  1482. #ifdef __32BIT__
  1483. static ULONG count;
  1484. static FILEFINDBUF3 find;
  1485. #define DosFindFirst(p1, p2, p3, p4, p5, p6) \
  1486.         DosFindFirst(p1, p2, p3, p4, p5, p6, 1)
  1487. #define DosQCurDisk     DosQueryCurrentDisk
  1488. #define DosQFSAttach    DosQueryFSAttach
  1489. #define DosQFSInfo      DosQueryFSInfo
  1490. #else
  1491. static USHORT count;
  1492. static FILEFINDBUF find;
  1493. #define DosFindFirst(p1, p2, p3, p4, p5, p6) \
  1494.         DosFindFirst(p1, p2, p3, p4, p5, p6, 0)
  1495. #define DosQFSAttach(p1, p2, p3, p4, p5) \
  1496.         DosQFSAttach(p1, p2, p3, p4, p5, 0)
  1497. #endif
  1498.  
  1499. int IsFileSystemFAT(char *dir)
  1500. {
  1501.   static USHORT nLastDrive = -1, nResult;
  1502.   ULONG lMap;
  1503.   BYTE bData[64], bName[3];
  1504. #ifdef __32BIT__
  1505.   ULONG nDrive, cbData;
  1506.   FSQBUFFER2 *pData = (FSQBUFFER2 *) bData;
  1507. #else
  1508.   USHORT nDrive, cbData;
  1509.   FSQBUFFER *pData = (FSQBUFFER *) bData;
  1510. #endif
  1511.  
  1512.   /* We separate FAT and HPFS file systems here. */
  1513.  
  1514.   if ( isalpha(dir[0]) && (dir[1] == ':') )
  1515.     nDrive = toupper(dir[0]) - '@';
  1516.   else
  1517.     DosQCurDisk(&nDrive, &lMap);
  1518.  
  1519.   if ( nDrive == nLastDrive )
  1520.     return nResult;
  1521.  
  1522.   bName[0] = (char) (nDrive + '@');
  1523.   bName[1] = ':';
  1524.   bName[2] = 0;
  1525.  
  1526.   nLastDrive = nDrive;
  1527.   cbData = sizeof(bData);
  1528.  
  1529.   if ( !DosQFSAttach(bName, 0, FSAIL_QUERYNAME, (PVOID) pData, &cbData) )
  1530.     nResult = !strcmp(pData -> szFSDName + pData -> cbName, "FAT");
  1531.   else
  1532.     nResult = FALSE;
  1533.  
  1534.   return nResult;
  1535. }
  1536.  
  1537. DIR *opendir(char *name)
  1538. {
  1539.   struct stat statb;
  1540.   DIR *dirp;
  1541.   char c;
  1542.   char *s;
  1543.   struct _dircontents *dp;
  1544.   char nbuf[MAXPATHLEN + 1];
  1545.   int len;
  1546.  
  1547.   strcpy(nbuf, name);
  1548.   len = strlen (nbuf);
  1549.   s = nbuf + len;
  1550.  
  1551.   if ( ((c = nbuf[strlen(nbuf) - 1]) == '\\' || c == '/') &&
  1552.        (strlen(nbuf) > 1) )
  1553.   {
  1554.     nbuf[strlen(nbuf) - 1] = 0;
  1555.  
  1556.     if ( nbuf[strlen(nbuf) - 1] == ':' )
  1557.       strcat(nbuf, "\\.");
  1558.   }
  1559.   else
  1560.     if ( nbuf[strlen(nbuf) - 1] == ':' )
  1561.       strcat(nbuf, ".");
  1562.  
  1563.   if (stat(nbuf, &statb) < 0 || (statb.st_mode & S_IFMT) != S_IFDIR)
  1564.     return NULL;
  1565.  
  1566.   if ( (dirp = malloc(sizeof(DIR))) == NULL )
  1567.     return NULL;
  1568.  
  1569.   if ( nbuf[strlen(nbuf) - 1] == '.' )
  1570.     strcpy(nbuf + strlen(nbuf) - 1, "*.*");
  1571.   else
  1572.     if ( ((c = nbuf[strlen(nbuf) - 1]) == '\\' || c == '/') &&
  1573.          (strlen(nbuf) == 1) )
  1574.       strcat(nbuf, "*.*");
  1575.     else
  1576.       strcat(nbuf, "\\*.*");
  1577.  
  1578.   dirp -> dd_loc = 0;
  1579.   dirp -> dd_contents = dirp -> dd_cp = NULL;
  1580.  
  1581.   if ((s = getdirent(nbuf)) == NULL)
  1582.     return dirp;
  1583.  
  1584.   do
  1585.   {
  1586.     if (((dp = malloc(sizeof(struct _dircontents))) == NULL) ||
  1587.         ((dp -> _d_entry = malloc(strlen(s) + 1)) == NULL)      )
  1588.     {
  1589.       if (dp)
  1590.         free(dp);
  1591.       free_dircontents(dirp -> dd_contents);
  1592.  
  1593.       return NULL;
  1594.     }
  1595.  
  1596.     if (dirp -> dd_contents)
  1597.     {
  1598.       dirp -> dd_cp -> _d_next = dp;
  1599.       dirp -> dd_cp = dirp -> dd_cp -> _d_next;
  1600.     }
  1601.     else
  1602.       dirp -> dd_contents = dirp -> dd_cp = dp;
  1603.  
  1604.     strcpy(dp -> _d_entry, s);
  1605.     dp -> _d_next = NULL;
  1606.  
  1607.     dp -> _d_size = find.cbFile;
  1608.     dp -> _d_mode = find.attrFile;
  1609.     dp -> _d_time = *(unsigned *) &(find.ftimeLastWrite);
  1610.     dp -> _d_date = *(unsigned *) &(find.fdateLastWrite);
  1611.   }
  1612.   while ((s = getdirent(NULL)) != NULL);
  1613.  
  1614.   dirp -> dd_cp = dirp -> dd_contents;
  1615.  
  1616.   return dirp;
  1617. }
  1618.  
  1619.  
  1620. void closedir(DIR * dirp)
  1621. {
  1622.   free_dircontents(dirp -> dd_contents);
  1623.   free(dirp);
  1624. }
  1625.  
  1626.  
  1627. struct dirent *readdir(DIR * dirp)
  1628. {
  1629.   static struct dirent dp;
  1630.  
  1631.   if (dirp -> dd_cp == NULL)
  1632.     return NULL;
  1633.  
  1634.   dp.d_namlen = dp.d_reclen =
  1635.     strlen(strcpy(dp.d_name, dirp -> dd_cp -> _d_entry));
  1636.  
  1637.   dp.d_ino = 1;
  1638.  
  1639.   dp.d_size = dirp -> dd_cp -> _d_size;
  1640.   dp.d_mode = dirp -> dd_cp -> _d_mode;
  1641.   dp.d_time = dirp -> dd_cp -> _d_time;
  1642.   dp.d_date = dirp -> dd_cp -> _d_date;
  1643.  
  1644.   dirp -> dd_cp = dirp -> dd_cp -> _d_next;
  1645.   dirp -> dd_loc++;
  1646.  
  1647.   return &dp;
  1648. }
  1649.  
  1650.  
  1651. void seekdir(DIR * dirp, long off)
  1652. {
  1653.   long i = off;
  1654.   struct _dircontents *dp;
  1655.  
  1656.   if (off >= 0)
  1657.   {
  1658.     for (dp = dirp -> dd_contents; --i >= 0 && dp; dp = dp -> _d_next);
  1659.  
  1660.     dirp -> dd_loc = off - (i + 1);
  1661.     dirp -> dd_cp = dp;
  1662.   }
  1663. }
  1664.  
  1665.  
  1666. long telldir(DIR * dirp)
  1667. {
  1668.   return dirp -> dd_loc;
  1669. }
  1670.  
  1671.  
  1672. static void free_dircontents(struct _dircontents * dp)
  1673. {
  1674.   struct _dircontents *odp;
  1675.  
  1676.   while (dp)
  1677.   {
  1678.     if (dp -> _d_entry)
  1679.       free(dp -> _d_entry);
  1680.  
  1681.     dp = (odp = dp) -> _d_next;
  1682.     free(odp);
  1683.   }
  1684. }
  1685.  
  1686.  
  1687. char *getdirent(char *dir)
  1688. {
  1689.   int done;
  1690.   static int lower = TRUE;
  1691.  
  1692.   if (dir != NULL)
  1693.   {                       /* get first entry */
  1694.     lower = IsFileSystemFAT(dir);
  1695.  
  1696.     hdir = HDIR_CREATE;
  1697.     count = 1;
  1698.     done = DosFindFirst(dir, &hdir, attributes, &find, sizeof(find), &count);
  1699.   }
  1700.   else                       /* get next entry */
  1701.     done = DosFindNext(hdir, &find, sizeof(find), &count);
  1702.  
  1703.   if (done == 0)
  1704.   {
  1705.     if ( lower )
  1706.       strlwr(find.achName);
  1707.     return find.achName;
  1708.   }
  1709.   else
  1710.   {
  1711.     DosFindClose(hdir);
  1712.     return NULL;
  1713.   }
  1714. }
  1715.  
  1716. #endif /* __EMX__ */
  1717.  
  1718. #ifdef __IBMC__
  1719.  
  1720. /* quick hack because IBM C lacks popen() and pclose() */
  1721.  
  1722. int pids[64];
  1723.  
  1724. FILE *
  1725. popen(char *cmd, char *mode) {
  1726.   HFILE end1, end2, std, old1, old2, temp;
  1727.   FILE *file;
  1728.   char fail[256], cmd_line[256], *cmd_exe, *args;
  1729.   RESULTCODES res;
  1730.   int rc;
  1731.  
  1732.   if (DosCreatePipe(&end1, &end2, 4096))
  1733.     return NULL;
  1734.  
  1735.   std = (*mode == 'w') ? 0 /* stdin */ : 1 /* stdout */;
  1736.   if (std == 0) {
  1737.     temp = end1; end1 = end2; end2 = temp;
  1738.   }
  1739.  
  1740.   old1 = -1; /* save stdin or stdout */
  1741.   DosDupHandle(std, &old1);
  1742.   DosSetFHState(old1, OPEN_FLAGS_NOINHERIT);
  1743.   temp = std; /* redirect stdin or stdout */
  1744.   DosDupHandle(end2, &temp);
  1745.  
  1746.   if ( std == 1 ) {
  1747.     old2 = -1; /* save stderr */
  1748.     DosDupHandle(2, &old2);
  1749.     DosSetFHState(old2, OPEN_FLAGS_NOINHERIT);
  1750.     temp = 2;   /* redirect stderr */
  1751.     DosDupHandle(end2, &temp);
  1752.   }
  1753.  
  1754.   DosClose(end2);
  1755.   DosSetFHState(end1, OPEN_FLAGS_NOINHERIT);
  1756.  
  1757.   if ( (cmd_exe = getenv("COMSPEC")) == NULL )
  1758.     cmd_exe = "cmd.exe";
  1759.  
  1760.   strcpy(cmd_line, cmd_exe);
  1761.   args = cmd_line + strlen(cmd_line) + 1; /* skip zero */
  1762.   strcpy(args, "/c ");
  1763.   strcat(args, cmd);
  1764.   args[strlen(args) + 1] = '\0'; /* two zeroes */
  1765.   rc = DosExecPgm(fail, sizeof(fail), EXEC_ASYNCRESULT, 
  1766.           cmd_line, 0, &res, cmd_exe);
  1767.  
  1768.   temp = std; /* restore stdin or stdout */
  1769.   DosDupHandle(old1, &temp);
  1770.   DosClose(old1);
  1771.  
  1772.   if ( std == 1 ) {
  1773.     temp = 2;   /* restore stderr */
  1774.     DosDupHandle(old2, &temp);
  1775.     DosClose(old2);
  1776.   }
  1777.  
  1778.   if (rc) {
  1779.     DosClose(end1);
  1780.     return NULL;
  1781.   }
  1782.   
  1783.   file = fdopen(end1, mode);
  1784.   pids[end1] = res.codeTerminate;
  1785.   return file;
  1786. }
  1787.  
  1788. int
  1789. pclose(FILE *pipe) {
  1790.   RESULTCODES rc;
  1791.   PID pid;
  1792.   int handle = fileno(pipe);
  1793.   fclose(pipe);
  1794.   if (pids[handle])
  1795.     DosWaitChild(DCWA_PROCESSTREE, DCWW_WAIT, &rc, &pid, pids[handle]);
  1796.   pids[handle] = 0;
  1797.   return rc.codeTerminate == 0 ? rc.codeResult : -1;
  1798. }
  1799. #endif
  1800.  
  1801. void
  1802. ChangeNameForFAT(char *name) {
  1803.   char *src, *dst, *next, *ptr, *dot, *start;
  1804.   static char invalid[] = ":;,=+\"[]<>| \t";
  1805.  
  1806.   if ( isalpha(name[0]) && (name[1] == ':') )
  1807.     start = name + 2;
  1808.   else
  1809.     start = name;
  1810.  
  1811.   src = dst = start;
  1812.   if ( (*src == '/') || (*src == '\\') )
  1813.     src++, dst++;
  1814.  
  1815.   while ( *src )
  1816.   {
  1817.     for ( next = src; *next && (*next != '/') && (*next != '\\'); next++ );
  1818.  
  1819.     for ( ptr = src, dot = NULL; ptr < next; ptr++ )
  1820.       if ( *ptr == '.' )
  1821.       {
  1822.         dot = ptr; /* remember last dot */
  1823.         *ptr = '_';
  1824.       }
  1825.  
  1826.     if ( dot == NULL )
  1827.       for ( ptr = src; ptr < next; ptr++ )
  1828.         if ( *ptr == '_' )
  1829.           dot = ptr; /* remember last _ as if it were a dot */
  1830.  
  1831.     if ( dot && (dot > src) &&
  1832.          ((next - dot <= 4) ||
  1833.           ((next - src > 8) && (dot - src > 3))) )
  1834.     {
  1835.       if ( dot )
  1836.         *dot = '.';
  1837.  
  1838.       for ( ptr = src; (ptr < dot) && ((ptr - src) < 8); ptr++ )
  1839.         *dst++ = *ptr;
  1840.  
  1841.       for ( ptr = dot; (ptr < next) && ((ptr - dot) < 4); ptr++ )
  1842.         *dst++ = *ptr;
  1843.     }
  1844.     else
  1845.     {
  1846.       if ( dot && (next - src == 1) )
  1847.         *dot = '.';           /* special case: "." as a path component */
  1848.  
  1849.       for ( ptr = src; (ptr < next) && ((ptr - src) < 8); ptr++ )
  1850.         *dst++ = *ptr;
  1851.     }
  1852.  
  1853.     *dst++ = *next; /* either '/' or 0 */
  1854.  
  1855.     if ( *next )
  1856.     {
  1857.       src = next + 1;
  1858.  
  1859.       if ( *src == 0 ) /* handle trailing '/' on dirs ! */
  1860.         *dst = 0;
  1861.     }
  1862.     else
  1863.       break;
  1864.   }
  1865.  
  1866.   for ( src = start; *src != 0; ++src )
  1867.     if ( strchr(invalid, *src) != NULL )
  1868.         *src = '_';
  1869. }
  1870.  
  1871.  
  1872. int IsFileNameValid(char *name)
  1873. {
  1874.   HFILE hf;
  1875. #ifdef __32BIT__
  1876.   ULONG uAction;
  1877. #else
  1878.   USHORT uAction;
  1879. #endif
  1880.  
  1881.   switch( DosOpen(name, &hf, &uAction, 0, 0, FILE_OPEN,
  1882.                   OPEN_ACCESS_READONLY | OPEN_SHARE_DENYNONE, 0) )
  1883.   {
  1884.   case ERROR_INVALID_NAME:
  1885.   case ERROR_FILENAME_EXCED_RANGE:
  1886.     return FALSE;
  1887.   case NO_ERROR:
  1888.     DosClose(hf);
  1889.   default:
  1890.     return TRUE;
  1891.   }
  1892. }
  1893.  
  1894. long
  1895. zdskspace(int drive) {
  1896.   FSALLOCATE fsa;
  1897.   if ( DosQFSInfo(drive, 1, (PBYTE) &fsa, sizeof(fsa)) )
  1898.     return 0;
  1899.   return fsa.cUnitAvail * fsa.cSectorUnit * fsa.cbSector;
  1900. }
  1901.  
  1902. char *
  1903. GetLoadPath(void) {
  1904. #ifdef __32BIT__
  1905.   PTIB pptib;
  1906.   PPIB pppib;
  1907.   char *szPath;
  1908.  
  1909.   DosGetInfoBlocks(&pptib, &pppib);
  1910.  
  1911.   szPath = pppib -> pib_pchenv;
  1912.  
  1913.   while (*szPath)
  1914.     szPath = strchr(szPath, 0) + 1;
  1915.  
  1916.   return szPath + 1;
  1917. #else
  1918.   extern char *_pgmptr;
  1919.   return _pgmptr;
  1920. #endif
  1921. }
  1922.